////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//         This file is distributed under the BSD 2-Clause open source license. See Licenses/License.txt for details.         //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "WNScripting/inc/WNReturn.h"
#include "WNScripting/inc/WNCodeModule.h"
#include "WNScripting/inc/WNExpression.h"
#include "WNScripting/inc/WNTypeManager.h"
#include "WNScripting/inc/WNScopedVariableList.h"
#include "WNScripting/inc/WNNodeHelpers.h"

#ifdef _WN_MSVC
    #pragma warning(push)
    #pragma warning(disable: 4100)
    #pragma warning(disable: 4127)
    #pragma warning(disable: 4152)
    #pragma warning(disable: 4244)
    #pragma warning(disable: 4245)
    #pragma warning(disable: 4267)
    #pragma warning(disable: 4355)
    #pragma warning(disable: 4512)
#endif

#include "llvm/IR/IRBuilder.h"

#ifdef _WN_MSVC
    #pragma warning(pop)
#endif

using namespace WNScripting; 

WNReturn::WNReturn(WNExpression* _expr):
    mExpression(_expr) {
}

WNReturn::WNReturn() :
    mExpression(WN_NULL) {
}

WNReturn::~WNReturn() {
    if(mExpression) {
        WN_DELETE(mExpression);
    }
}

eWNTypeError WNReturn::GenerateCode(WNCodeModule& _module, const WNFunctionDefinition* _def, WNLogging::WNLog& _compilationLog) {
    llvm::IRBuilder<>* builder = reinterpret_cast<llvm::IRBuilder<>*>(_module.GetBuilder());
    mReturns = true;
    eWNTypeError err;
    if(mExpression) {
        llvm::Value* v = WN_NULL;
        if(eWNOK != (err = mExpression->GenerateCode(_module, _def, _compilationLog)) ) {
            return(err);
        }
        v  = mExpression->GetValue();
        if(_def->mReturn != mExpression->GetType()) {
            const GenerateCastingOperation* castOp = _module.GetTypeManager().GetCastingOperation(mExpression->GetType(), _def->mReturn);
            if(!castOp) {
                _compilationLog.Log(WNLogging::eError, 0, "Cannot cast ", mExpression->GetType(), " to ", _def->mReturn->mName);
                LogLine(_compilationLog, WNLogging::eError);
                return(eWNInvalidCast);
            }
            if(eWNOK != (err = castOp->Execute(_module.GetBuilder(), mExpression->GetValue(), v))) {
                _compilationLog.Log(WNLogging::eCritical, 0, "Error generating casting operation ", mExpression->GetType()->mName, " to ", _def->mReturn->mName);
                LogLine(_compilationLog, WNLogging::eCritical);
                return(err);
            }
        }
        if(eWNOK != (err = _module.GetScopedVariableList().GenerateReturn(_module, _def, _compilationLog))) {
            _compilationLog.Log(WNLogging::eCritical, 0, "Error generating return");
            LogLine(_compilationLog, WNLogging::eCritical);
            return(err);
        }
        builder->CreateRet(v);
    } else {
        if(eWNOK != (err = _module.GetScopedVariableList().GenerateReturn(_module, _def, _compilationLog))) {
            _compilationLog.Log(WNLogging::eCritical, 0, "Error generating return");
            LogLine(_compilationLog, WNLogging::eCritical);
            return(err);
        }
        if(eWNOK != (err = GenerateVOIDReturn(_module, _def))) {
            _compilationLog.Log(WNLogging::eCritical, 0, "Error generating void return");
            LogLine(_compilationLog, WNLogging::eCritical);
            return(err);
        }
    }
    return(eWNOK);
}

